home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / HPACK78S.ZIP / arcdirio.c < prev    next >
C/C++ Source or Header  |  1992-12-03  |  47KB  |  1,538 lines

  1. /****************************************************************************
  2. *                                                                            *
  3. *                          HPACK Multi-System Archiver                        *
  4. *                          ===========================                        *
  5. *                                                                            *
  6. *                         Archive Directory I/O Routines                        *
  7. *                          ARCDIRIO.C  Updated 20/08/92                        *
  8. *                                                                            *
  9. * This program is protected by copyright and as such any use or copying of    *
  10. *  this code for your own purposes directly or indirectly is highly uncool    *
  11. *                    and if you do so there will be....trubble.                *
  12. *               And remember: We know where your kids go to school.            *
  13. *                                                                            *
  14. *        Copyright 1989 - 1992  Peter C.Gutmann.  All rights reserved        *
  15. *                                                                            *
  16. ****************************************************************************/
  17.  
  18. #include <ctype.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #ifdef __MAC__
  22.   #include "defs.h"
  23.   #include "arcdir.h"
  24.   #include "choice.h"
  25.   #include "error.h"
  26.   #include "flags.h"
  27.   #include "frontend.h"
  28.   #include "hpacklib.h"
  29.   #include "hpaktext.h"
  30.   #include "system.h"
  31.   #include "crc16.h"
  32.   #include "crypt.h"
  33.   #include "fastio.h"
  34.   #include "hpackio.h"
  35.   #include "store.h"
  36. #else
  37.   #include "defs.h"
  38.   #include "arcdir.h"
  39.   #include "choice.h"
  40.   #include "error.h"
  41.   #include "flags.h"
  42.   #include "frontend.h"
  43.   #include "hpacklib.h"
  44.   #include "system.h"
  45.   #include "crc/crc16.h"
  46.   #include "crypt/crypt.h"
  47.   #include "io/fastio.h"
  48.   #include "io/hpackio.h"
  49.   #include "language/hpaktext.h"
  50.   #include "store/store.h"
  51. #endif /* __MAC__ */
  52.  
  53. /* Prototypes for functions in ARCDIR.C */
  54.  
  55. void addDirHdrToList( DIRHDRLIST *theHeader );
  56. void fixEverything( void );
  57.  
  58. /* Prototypes for functions in ARCHIVE.C */
  59.  
  60. void blankLine( int length );
  61. void idiotBox( const char *message );
  62. BOOLEAN confirmAction( const char *message );
  63.  
  64. /* The following are declared in ARCDIR.C */
  65.  
  66. extern BOOLEAN doFixEverything;
  67. extern LONG fileDataOffset, dirDataOffset;
  68. extern DIRHDRLIST **dirHdrList;
  69. extern int currDirHdrListIndex, lastEntry;
  70.  
  71. /* The size of the memory buffer for archive information */
  72.  
  73. #if ARCHIVE_TYPE == 1
  74.   #define MEM_BUFSIZE    1000    /* More modest memory usage in test vers. */
  75. #else
  76.   #define MEM_BUFSIZE    8000
  77. #endif /* ARCHIVE_TYPE == 1 */
  78.  
  79. /* The structure which holds the archive trailer info.  The checksum field
  80.    overlays the security field length for secured archives */
  81.  
  82. typedef struct {
  83.                WORD noDirHdrs;            /* No.of directory headers */
  84.                WORD noFileHdrs;            /* No.of file headers */
  85.                LONG dirInfoSize;        /* Length of dir.info.block */
  86.                WORD checksum;            /* CRC16 of all preceding dir.data */
  87.                BYTE specialInfo;        /* The kludge byte */
  88.                BYTE archiveID[ 4 ];        /* 'HPAK' */
  89.                } ARCHIVE_TRAILER;
  90.  
  91. #define TRAILER_SIZE        ( sizeof( WORD ) + sizeof( WORD ) + sizeof( LONG ) + \
  92.                               sizeof( WORD ) + sizeof( BYTE ) + HPACK_ID_SIZE )
  93.  
  94. /* The size of the trailer for multipart and/or secured archives, and the
  95.    magic bit set in the multipart trailer which indicates that the
  96.    segmentation info and final trailer are on a seperate disk */
  97.  
  98. #define SHORT_TRAILER_SIZE    ( sizeof( WORD ) + sizeof( WORD ) + sizeof( LONG ) + \
  99.                               sizeof( WORD ) )
  100.  
  101. #define MULTIPART_SEPERATE_DISK        0x8000
  102.  
  103. /* The size of the checksummable information in the trailer */
  104.  
  105. #define TRAILER_CHK_SIZE    ( sizeof( WORD ) + sizeof( WORD ) + sizeof( LONG ) )
  106.  
  107. /* The size of the multipart/secured archive final trailer */
  108.  
  109. #define MULTIPART_TRAILER_SIZE    ( sizeof( WORD ) + sizeof( BYTE ) + HPACK_ID_SIZE )
  110. #define SECURED_TRAILER_SIZE    MULTIPART_TRAILER_SIZE
  111.  
  112. /* The magic ID and temporary name extensions for HPACK archives */
  113.  
  114. char HPACK_ID[] = "HPAK";
  115. #ifdef __ARC__
  116.   char TEMP_EXT[] = "?1";
  117.   char DIRTEMP_EXT[] = "?2";
  118.   char SECTEMP_EXT[] = "?3";
  119. #else
  120.   char TEMP_EXT[] = "$$1";
  121.   char DIRTEMP_EXT[] = "$$2";
  122.   char SECTEMP_EXT[] = "$$3";
  123. #endif /* __ARC__ */
  124.  
  125. /* A special value for the fileKludge to indicate the prototype versions of
  126.    either the LZW, LZA, or MBWA versions of HPACK */
  127.  
  128. #if ARCHIVE_TYPE == 1
  129.   #define PROTOTYPE    0x20                /* LZW development version */
  130. #elif ARCHIVE_TYPE == 2
  131.   #define PROTOTYPE    0x40                /* LZA development version */
  132. #elif ARCHIVE_TYPE == 3
  133.   #define PROTOTYPE    0x60                /* LZA' release version */
  134. #elif ARCHIVE_TYPE == 4
  135.   #define PROTOTYPE    0x80                /* LZA" release version */
  136. #elif ARCHIVE_TYPE == 5
  137.   #define PROTOTYPE    0xA0                /* MBWA prototype version 1 */
  138. #elif ARCHIVE_TYPE == 6
  139.   #define PROTOTYPE    0xC0                /* MBWA prototype version 2 */
  140. #else
  141.   #error "Need to define ARCHIVE_TYPE"
  142. #endif /* Various ARCHIVE_TYPE-specific defines */
  143.  
  144. #define isPrototype(value)    ( ( value ) & 0xE0 )/* Whether this archive was
  145.                                                     created by a proto-HPACK */
  146.  
  147. /* Symbolic defines to specify whether we want getArchiveInfo() to read in
  148.    the ID bytes or not */
  149.  
  150. #define READ_ID        TRUE
  151. #define NO_READ_ID    FALSE
  152.  
  153. /* Defines to handle the ^Z padding which some versions of Xmodem/Ymodem do */
  154.  
  155. #define CPM_EOF                0x1A
  156. #define    YMODEM_BLOCKSIZE    1024    /* We can have up to this many ^Z's
  157.                                        appended */
  158.  
  159. /****************************************************************************
  160. *                                                                            *
  161. *                                Global Variables                            *
  162. *                                                                            *
  163. ****************************************************************************/
  164.  
  165. PARTSIZELIST *partSizeStartPtr, *partSizeCurrPtr;
  166.         /* Start and current position in the list of archive parts */
  167.  
  168. int currPart;                        /* Current part no.of multipart archive */
  169. int lastPart;                        /* Last part no.of multipart archive */
  170. long segmentEnd;                    /* End of current archive segment */
  171. long endPosition;                    /* Virtual end of archive */
  172.  
  173. static ARCHIVE_TRAILER archiveInfo;    /* The archive trailer info */
  174.  
  175. /****************************************************************************
  176. *                                                                            *
  177. *                        Read a Directory from a File                        *
  178. *                                                                            *
  179. ****************************************************************************/
  180.  
  181. /* Whether the archive directory passes a sanity check when we read it.
  182.    Note that each of the four routines which reads in file and directory
  183.    headers and names contains a sanity check exit-condition in case we
  184.    run out of input data.  In addition, various routines in ARCDIR.C also
  185.    perform sanity checking.  It may be useful at some point to change the
  186.    BOOLEAN to an int which is incremented each time a check fails to indicate
  187.    the seriousness of the problem */
  188.  
  189. BOOLEAN arcdirCorrupted;
  190.  
  191. /* Read the file headers from a file */
  192.  
  193. static void readFileHeaders( WORD noFileHdrs )
  194.     {
  195.     FILEHDR theHeader;
  196.     WORD hType, linkID;
  197.     BYTE extraInfo;
  198.     int extraInfoLen = 0, extraInfoIndex;
  199.  
  200.     while( noFileHdrs-- )
  201.         {
  202.         /* Assemble each header and add it to the directory tree, with
  203.            sanity check */
  204.         hType = TYPE_NORMAL;        /* Reset header hType */
  205.         extraInfo = linkID = 0;        /* Reset extraInfo and linkID */
  206.         if( ( theHeader.archiveInfo = fgetWord() ) == ( WORD ) FEOF )
  207.             {
  208.             arcdirCorrupted = TRUE;
  209.             return;
  210.             }
  211.         switch( theHeader.archiveInfo & ARCH_OTHER_LEN )
  212.             {
  213.             case ARCH_OTHER_ZERO_ZERO:
  214.                 /* dirIndex = ROOT_DIR, auxDataLen = 0 */
  215.                 theHeader.dirIndex = ROOT_DIR;
  216.                 theHeader.auxDataLen = 0L;
  217.                 break;
  218.  
  219.             case ARCH_OTHER_BYTE_BYTE:
  220.                 /* dirIndex = BYTE, auxDataLen = BYTE */
  221.                 theHeader.dirIndex = ( WORD ) fgetByte();
  222.                 theHeader.auxDataLen = ( LONG ) fgetByte();
  223.                 break;
  224.  
  225.             case ARCH_OTHER_BYTE_WORD:
  226.                 /* dirIndex = BYTE, auxDataLen = WORD */
  227.                 theHeader.dirIndex = ( WORD ) fgetByte();
  228.                 theHeader.auxDataLen = ( LONG ) fgetWord();
  229.                 break;
  230.  
  231.             case ARCH_OTHER_WORD_LONG:
  232.                 /* dirIndex = WORD, auxDataLen = LONG */
  233.                 theHeader.dirIndex = fgetWord();
  234.                 theHeader.auxDataLen = fgetLong();
  235.             }
  236.  
  237.         theHeader.fileTime = fgetLong();
  238.         if( theHeader.archiveInfo & ARCH_ORIG_LEN )
  239.             theHeader.fileLen = fgetLong();
  240.         else
  241.             theHeader.fileLen = ( LONG ) fgetWord();
  242.         if( theHeader.archiveInfo & ARCH_COPR_LEN )
  243.             theHeader.dataLen = fgetLong();
  244.         else
  245.             theHeader.dataLen = ( LONG ) fgetWord();
  246.  
  247.         /* Handle type SPECIAL files by reading in the extra WORD giving
  248.            the file's hType */
  249.         if( theHeader.archiveInfo & ARCH_SPECIAL )
  250.             hType = fgetWord();
  251.  
  252.         /* Handle any extra information attached to the header */
  253.         if( theHeader.archiveInfo & ARCH_EXTRAINFO )
  254.             {
  255.             /* Get extra info.byte and extract length information */
  256.             extraInfo = fgetByte();
  257.             extraInfoLen = getExtraInfoLen( extraInfo );
  258.             extraInfoIndex = 1;        /* Data follows extraInfo byte */
  259.             }
  260.  
  261.         /* Read link ID if there is one */
  262.         if( extraInfo & EXTRA_LINKED )
  263.             linkID = fgetWord();
  264.  
  265.         addFileHeader( &theHeader, hType, extraInfo, linkID );
  266.         fileHdrCurrPtr->tagged = FALSE;
  267.  
  268.         /* Read in any extra information attached to the header if necessary */
  269.         if( extraInfoLen )
  270.             {
  271. #if defined( __MSDOS__ )
  272.             /* Fix stupid compiler bug */
  273. #elif defined( __ARC__ )
  274.             if( ( theHeader.archiveInfo & ARCH_SYSTEM ) == OS_ARCHIMEDES )
  275.                 {
  276.                 /* Read in file type */
  277.                 extraInfoLen -= sizeof( WORD );
  278.                 if( extraInfoLen < 0 )
  279.                     {
  280.                     /* Length info has been corrupted */
  281.                     extraInfoLen = 0;
  282.                     fileHdrCurrPtr->type = 0;
  283.                     }
  284.                 else
  285.                     fileHdrCurrPtr->type = fgetWord();
  286.                 }
  287.             else
  288.                 /* File type is unknown */
  289.                 fileHdrCurrPtr->type = 0;
  290. #elif defined( __IIGS__ )
  291.             if( ( theHeader.archiveInfo & ARCH_SYSTEM ) == OS_IIGS )
  292.                 {
  293.                 /* Read in file type and auxiliary type */
  294.                 extraInfoLen -= sizeof( WORD ) + sizeof( LONG );
  295.                 if( extraInfoLen < 0 )
  296.                     {
  297.                     /* Length info has been corrupted */
  298.                     extraInfoLen = 0;
  299.                     fileHdrCurrPtr->type = 0;
  300.                     fileHdrCurrPtr->auxType = 0L;
  301.                     }
  302.                 else
  303.                     {
  304.                     fileHdrCurrPtr->type = fgetWord();
  305.                     fileHdrCurrPtr->auxType = fgetLong();
  306.                     }
  307.                 }
  308.             else
  309.                 {
  310.                 /* File type and auxiliary type are unknown */
  311.                 fileHdrCurrPtr->type = 0;
  312.                 fileHdrCurrPtr->auxType = 0L;
  313.                 }
  314. #elif defined( __MAC__ )
  315.             if( ( theHeader.archiveInfo & ARCH_SYSTEM ) == OS_MAC )
  316.                 {
  317.                 /* Read in file type and creator */
  318.                 extraInfoLen -= sizeof( LONG ) + sizeof( LONG );
  319.                 if( extraInfoLen < 0 )
  320.                     {
  321.                     /* Length info has been corrupted, default to type 'TEXT' */
  322.                     extraInfoLen = 0;
  323.                     fileHdrCurrPtr->type = fileHdrCurrPtr->creator = 'TEXT';
  324.                     }
  325.                 else
  326.                     {
  327.                     fileHdrCurrPtr->type = fgetLong();
  328.                     fileHdrCurrPtr->creator = fgetLong();
  329.                     }
  330.                 }
  331.             else
  332.                 /* File type and creator are unknown.  Note that we set the
  333.                    type to 0L rather than 'TEXT' since we may be able to
  334.                    pick up type information later on */
  335.                 fileHdrCurrPtr->type = fileHdrCurrPtr->creator = 0L;
  336. #elif defined( __UNIX__ )
  337.             if( ( theHeader.archiveInfo & ARCH_SYSTEM ) == OS_UNIX )
  338.                 {
  339.                 /* Read in link ID */
  340.                 extraInfoLen -= sizeof( WORD );
  341.                 if( extraInfoLen < 0 )
  342.                     {
  343.                     /* Length info has been corrupted */
  344.                     extraInfoLen = 0;
  345.                     fileHdrCurrPtr->fileLinkID = 0;
  346.                     }
  347.                 else
  348.                     fileHdrCurrPtr->fileLinkID = fgetWord();
  349.                 }
  350.             else
  351.                 /* No linkID given */
  352.                 fileHdrCurrPtr->fileLinkID = 0;
  353.             fileHdrCurrPtr->linkID = 0L;    /* No dev/inode for this file */
  354. #endif /* Various OS-dependant extraInfo reads */
  355.  
  356.             /* Read in any remaining information */
  357.             while( extraInfoLen-- )
  358.                 fileHdrCurrPtr->extraInfo[ extraInfoIndex++ ] = fgetByte();
  359.             extraInfoLen = 0;    /* Fix fencepost error */
  360.             }
  361.         else
  362.             {
  363. #if defined( __AMIGA__ ) || defined( __ATARI__ ) || defined( __MSDOS__ )
  364.             ;    /* Nothing */
  365. #elif defined( __ARC__ )
  366.             /* No file type */
  367.             fileHdrCurrPtr->type = 0;
  368. #elif defined( __IIGS__ )
  369.             /* No file type and auxiliary type */
  370.             fileHdrCurrPtr->type = 0;
  371.             fileHdrCurrPtr->auxType = 0L;
  372. #elif defined( __MAC__ )
  373.             /* No type/creator information */
  374.             fileHdrCurrPtr->type = fileHdrCurrPtr->creator = 0L;
  375. #elif defined( __UNIX__ )
  376.             /* No link information */
  377.             fileHdrCurrPtr->linkID = 0L;
  378. #endif /* Various OS-dependant extraInfo initializations */
  379.             }
  380.         }
  381.     }
  382.  
  383. /* Read the filenames from a file */
  384.  
  385. static void readFileNames( void )
  386.     {
  387.     FILEHDRLIST *prevPtr = NULL;
  388.  
  389.     /* Step along the chain of headers reading in the corresponding filenames */
  390.     for( fileHdrCurrPtr = fileHdrStartPtr; fileHdrCurrPtr != NULL; \
  391.          prevPtr = fileHdrCurrPtr, fileHdrCurrPtr = fileHdrCurrPtr->next )
  392.         addFileName( fileHdrCurrPtr->data.dirIndex, fileHdrCurrPtr->hType, NULL );
  393.  
  394.     fileHdrCurrPtr = prevPtr;    /* Reset currPos to last header */
  395.     }
  396.  
  397. /* Read the directory headers from a file */
  398.  
  399. static void readDirHeaders( WORD noDirHdrs )
  400.     {
  401.     WORD theEntry = ( WORD ) FEOF;
  402.     DIRHDRLIST *dirInfoPtr;
  403.     DIRHDR *theHeader;
  404.     int dirInfoFlags;
  405.  
  406.     if( noDirHdrs )
  407.         {
  408.         while( noDirHdrs-- )
  409.             {
  410.             /* Add each header to the directory tree */
  411.             if( ( dirInfoPtr = ( DIRHDRLIST * ) hmalloc( sizeof( DIRHDRLIST ) ) ) == NULL )
  412.                 error( OUT_OF_MEMORY );
  413.             currDirHdrListIndex++;
  414.             if( currDirHdrListIndex >= MEM_BUFSIZE )
  415.                 error( OUT_OF_MEMORY );
  416.             theHeader = &dirInfoPtr->data;
  417.  
  418.             /* Set up housekeeping info for the header */
  419.             dirHdrList[ currEntry ]->next = currEntry + 1;
  420.             dirHdrList[ ++currEntry ] = dirInfoPtr;
  421.             dirInfoPtr->offset = dirDataOffset;
  422.             dirInfoPtr->dirIndex = currEntry;
  423.             dirInfoPtr->hType = DIRTYPE_NORMAL;
  424.             dirInfoPtr->linkID = NO_LINK;
  425.  
  426.             /* Read the header information off disk, with sanity check */
  427.             if( ( dirInfoFlags = fgetByte() ) == FEOF )
  428.                 {
  429.                 arcdirCorrupted = TRUE;
  430.                 return;
  431.                 }
  432.             theHeader->dirInfo = dirInfoFlags;
  433.             switch( theHeader->dirInfo & DIR_OTHER_LEN )
  434.                 {
  435.                 case DIR_OTHER_ZERO_ZERO:
  436.                     /* parentIndex = ROOT_DIR, dataLen = 0 */
  437.                     theHeader->parentIndex = ROOT_DIR;
  438.                     theHeader->dataLen = 0L;
  439.                     break;
  440.  
  441.                 case DIR_OTHER_BYTE_BYTE:
  442.                     /* parentIndex = BYTE, dataLen = BYTE */
  443.                     theHeader->parentIndex = ( WORD ) fgetByte();
  444.                     theHeader->dataLen = ( LONG ) fgetByte();
  445.                     break;
  446.  
  447.                 case DIR_OTHER_BYTE_WORD:
  448.                     /* parentIndex = BYTE, dataLen = WORD */
  449.                     theHeader->parentIndex = ( WORD ) fgetByte();
  450.                     theHeader->dataLen = ( LONG ) fgetWord();
  451.                     break;
  452.  
  453.                 case DIR_OTHER_WORD_LONG:
  454.                     /* parentIndex = WORD, dataLen = LONG */
  455.                     theHeader->parentIndex = fgetWord();
  456.                     theHeader->dataLen = fgetLong();
  457.                 }
  458.             theHeader->dirTime = fgetLong();
  459.  
  460.             /* Read in type SPECIAL word and linkID if necessary */
  461.             if( theHeader->dirInfo & DIR_SPECIAL )
  462.                 dirInfoPtr->hType = fgetWord();
  463.             if( theHeader->dirInfo & DIR_LINKED )
  464.                 dirInfoPtr->linkID = fgetWord();
  465.  
  466.             dirInfoPtr->fileInDirListHead = dirInfoPtr->fileInDirListTail = NULL;
  467.             dirInfoPtr->dirInDirListHead = dirInfoPtr->dirInDirListTail = NULL;
  468.             dirInfoPtr->tagged = FALSE;
  469.             dirDataOffset += dirInfoPtr->data.dataLen;
  470.             }
  471.         dirInfoPtr->next = END_MARKER;
  472.         }
  473.     lastEntry = currEntry;
  474.  
  475.     /* We've read in all the directories, now create the list of directories
  476.        inside directories (this has to be done after all the directories are
  477.        read in since they will not necessarily be in the correct order as they
  478.        are read) */
  479.     for( theEntry = dirHdrList[ ROOT_DIR ]->next; theEntry != END_MARKER; \
  480.          theEntry = dirHdrList[ theEntry ]->next )
  481.         addDirHdrToList( dirHdrList[ theEntry ] );
  482.     }
  483.  
  484. /* Read the directory names from a file */
  485.  
  486. static void readDirNames( void )
  487.     {
  488.     WORD *wordPtr = ( WORD * ) mrglBuffer;
  489.     WORD theEntry;
  490.     int dirNameLength, ch;
  491.  
  492.     /* Read in directory names */
  493.     for( theEntry = dirHdrList[ ROOT_DIR ]->next; theEntry != END_MARKER; \
  494.          theEntry = dirHdrList[ theEntry ]->next )
  495.         {
  496.         /* Read in directory name with sanity check */
  497.         dirNameLength = 0;
  498.         if( dirHdrList[ theEntry ]->data.dirInfo & DIR_UNICODE )
  499.             {
  500.             /* Read Unicode directory name */
  501.             while( ch = fgetWord() )
  502.                 {
  503.                 /* Perform a sanity check for end of data or excessively
  504.                    long string */
  505.                 if( ch == FEOF || dirNameLength > ( DIRBUFSIZE / sizeof( WORD ) ) )
  506.                     {
  507.                     arcdirCorrupted = TRUE;
  508.                     return;
  509.                     }
  510.  
  511.                 wordPtr[ dirNameLength++ ] = ch;
  512.                 }
  513.  
  514.             wordPtr[ dirNameLength++ ] = 0x0000;
  515.             }
  516.         else
  517.             {
  518.             /* Read ASCII directory name */
  519.             while( ch = fgetByte() )
  520.                 {
  521.                 /* Perform a sanity check for end of data or excessively
  522.                    long string */
  523.                 if( ch == FEOF || dirNameLength > DIRBUFSIZE )
  524.                     {
  525.                     arcdirCorrupted = TRUE;
  526.                     return;
  527.                     }
  528.  
  529. #if defined( __AMIGA__ ) || defined( __ARC__ ) || defined( __MAC__ ) || \
  530.     defined( __OS2__ ) || defined( __UNIX__ )
  531.                 /* Check if we want to smash case */
  532.                 if( sysSpecFlags & SYSPEC_FORCELOWER )
  533.                     mrglBuffer[ dirNameLength++ ] = tolower( ch );
  534.                 else
  535. #endif /* __AMIGA__ || __ARC__ ||__MAC__ || __OS2__ || __UNIX__ */
  536.                 mrglBuffer[ dirNameLength++ ] = ch;
  537.                 }
  538.  
  539.             mrglBuffer[ dirNameLength++ ] = '\0';
  540.             }
  541.  
  542.         if( ( dirHdrList[ theEntry ]->dirName = \
  543.                     ( char * ) hmalloc( dirNameLength ) ) == NULL )
  544.             error( OUT_OF_MEMORY );
  545.         strcpy( dirHdrList[ theEntry ]->dirName, ( char * ) mrglBuffer );
  546.         }
  547.     }
  548.  
  549. /* Read in the section sizes for a multipart archive */
  550.  
  551. static void readPartSizes( void )
  552.     {
  553.     int count = lastPart;
  554.  
  555.     /* Read in each sections size and set the current part to the last part
  556.        read */
  557.     while( count-- )
  558.         addPartSize( fgetLong() );
  559.     currPart = lastPart;
  560.     }
  561.  
  562. /* Read the archive trailer information from a file */
  563.  
  564. static void getArchiveInfo( const BOOLEAN readID )
  565.     {
  566.     archiveInfo.noDirHdrs = fgetWord();
  567.     archiveInfo.noFileHdrs = fgetWord();
  568.     archiveInfo.dirInfoSize = fgetLong();
  569.     archiveInfo.checksum = fgetWord();
  570.  
  571.     if( readID )
  572.         {
  573.         archiveInfo.specialInfo = fgetByte();
  574.  
  575.         /* We must use fgetByte() to get the archive ID since fgetLong() will
  576.            do endianness conversion */
  577.         archiveInfo.archiveID[ 0 ] = fgetByte();
  578.         archiveInfo.archiveID[ 1 ] = fgetByte();
  579.         archiveInfo.archiveID[ 2 ] = fgetByte();
  580.         archiveInfo.archiveID[ 3 ] = fgetByte();
  581.         }
  582.     }
  583.  
  584. /* Save the directory data to a temporary file */
  585.  
  586. static void saveDirData( void )
  587.     {
  588.     WORD multipartFlagSave = multipartFlags;
  589.  
  590.     /* Turn off multipart writes when saving the directory data */
  591.     multipartFlags &= ~MULTIPART_WRITE;
  592.  
  593.     /* Open a temporary file (with the same reason given in FRONTEND.C for
  594.        using the archive name with the DIRTEMP_EXT suffix) and move the
  595.        directory data to it */
  596.     strcpy( dirFileName, archiveFileName );
  597.     strcpy( dirFileName + strlen( dirFileName ) - 3, DIRTEMP_EXT );
  598.     if( ( dirFileFD = hcreat( dirFileName, CREAT_ATTR ) ) == IO_ERROR )
  599.         error( CANNOT_OPEN_TEMPFILE );
  600.     setOutputFD( dirFileFD );
  601.     hlseek( archiveFD, fileDataOffset + HPACK_ID_SIZE, SEEK_SET );
  602.     moveData( dirDataOffset );
  603.     flushBuffer();
  604.     setOutputFD( archiveFD );
  605.  
  606.     multipartFlags = multipartFlagSave;
  607.     }
  608.  
  609. /*  Save the security data and final trailer to a temporary file */
  610.  
  611. static void saveSecData( void )
  612.     {
  613.     WORD multipartFlagSave = multipartFlags;
  614.  
  615.     /* Turn off multipart writes when saving the security data */
  616.     multipartFlags &= ~MULTIPART_WRITE;
  617.  
  618.     /* Open a temporary file and move the data as above */
  619.     strcpy( secFileName, archiveFileName );
  620.     strcpy( secFileName + strlen( secFileName ) - 3, SECTEMP_EXT );
  621.     if( ( secFileFD = hcreat( secFileName, CREAT_ATTR ) ) == IO_ERROR )
  622.         error( CANNOT_OPEN_TEMPFILE );
  623.     setOutputFD( secFileFD );
  624.     memcpy( _outBuffer, _inBuffer + SHORT_TRAILER_SIZE, \
  625.             secInfoLen + SECURED_TRAILER_SIZE );
  626.     writeBuffer( secInfoLen + SECURED_TRAILER_SIZE );
  627.     setOutputFD( archiveFD );
  628.  
  629.     multipartFlags = multipartFlagSave;
  630.     }
  631.  
  632. /* Read in the trailer information at the very end of an archive */
  633.  
  634. static int paddingSize;        /* No.bytes ^Z padding found by readArcTrailer() */
  635.  
  636. void readArcTrailer( void )
  637.     {
  638.     WORD multipartFlagSave, cryptFlagSave;
  639.     BOOLEAN notArchive;
  640.     int inByteCountSave, bytesRead;
  641. #ifdef GUI
  642.     char string[ 10 ];
  643. #endif /* GUI */
  644.  
  645.     /* If we are reading in the trailer of a multipart archive, move any data
  646.        in the input buffer which may be overwritten to a safe area and turn
  647.        off multipart reads */
  648.     if( flags & MULTIPART_ARCH )
  649.         {
  650.         memcpy( mrglBuffer, _inBuffer, HPACK_ID_SIZE + YMODEM_BLOCKSIZE );
  651.         multipartFlagSave = multipartFlags;
  652.         multipartFlags &= ~MULTIPART_READ;
  653.         cryptFlagSave = cryptFlags;
  654.         cryptFlags = 0;
  655.         inByteCountSave = _inByteCount;
  656.         }
  657.  
  658.     /* Make sure this is an HPACK archive */
  659.     paddingSize = 0;
  660.     hlseek( archiveFD, -( LONG ) TRAILER_SIZE, SEEK_END );
  661.     resetFastIn();
  662.     getArchiveInfo( READ_ID );
  663.     if( memcmp( archiveInfo.archiveID, HPACK_ID, HPACK_ID_SIZE ) )
  664.         {
  665.         notArchive = TRUE;
  666.         if( archiveInfo.archiveID[ 3 ] == CPM_EOF )
  667.             {
  668.             /* This may be a valid archive with CPM EOF's added to the end.
  669.                Try and find a valid HPACK ID before the ^Z's */
  670.             hlseek( archiveFD, -( LONG ) ( HPACK_ID_SIZE + YMODEM_BLOCKSIZE ), SEEK_END );
  671.             if( htell( archiveFD ) < 0L )
  672.                 /* We've gone past the start of the file; just seek to the
  673.                    start */
  674.                 hlseek( archiveFD, 0L, SEEK_SET );
  675.             bytesRead = hread( archiveFD, _inBuffer, HPACK_ID_SIZE + YMODEM_BLOCKSIZE );
  676.             for( paddingSize = bytesRead - 1; paddingSize >= HPACK_ID_SIZE && \
  677.                               _inBuffer[ paddingSize ] == CPM_EOF; paddingSize-- );
  678.             paddingSize++;    /* Fix fencepost error */
  679.             if( !memcmp( _inBuffer + paddingSize - HPACK_ID_SIZE, HPACK_ID, HPACK_ID_SIZE ) )
  680.                 {
  681.                 /* It is an HPACK archive, try to truncate the ^Z padding.  If
  682.                    we can't, remember the offset of the start of any useful data */
  683.                 notArchive = FALSE;
  684.                 paddingSize = bytesRead - paddingSize;
  685.                 hlseek( archiveFD, -( LONG ) paddingSize, SEEK_END );
  686.                 if( htruncate( archiveFD ) != IO_ERROR )
  687.                     {
  688. #ifdef GUI
  689.                     itoa( paddingSize, string, 10 );
  690.                     alert( ALERT_TRUNCATED_PADDING, string );
  691. #else
  692.                     hprintf( WARN_TRUNCATED_u_BYTES_EOF_PADDING, paddingSize );
  693. #endif /* GUI */
  694.                     paddingSize = 0;
  695.                     }
  696.  
  697.                 /* Re-read the trailer info */
  698.                 hlseek( archiveFD, -( long ) ( TRAILER_SIZE + paddingSize ), SEEK_END );
  699.                 resetFastIn();
  700.                 getArchiveInfo( READ_ID );
  701.                 }
  702.             }
  703.  
  704.         if( notArchive )
  705.             error( NOT_HPACK_ARCHIVE );
  706.         }
  707.  
  708.     /* If this is part of a multipart archive, set the correct part number */
  709.     if( archiveInfo.specialInfo & SPECIAL_MULTIPART )
  710.         currPart = archiveInfo.checksum;
  711.     if( archiveInfo.specialInfo & SPECIAL_MULTIEND )
  712.         currPart = lastPart;
  713.  
  714.     /* Restore the input buffer and turn multipart reads on again if necessary */
  715.     if( flags & MULTIPART_ARCH )
  716.         {
  717.         memcpy( _inBuffer, mrglBuffer, HPACK_ID_SIZE + YMODEM_BLOCKSIZE );
  718.         multipartFlags = multipartFlagSave;
  719.         cryptFlags = cryptFlagSave;
  720.         _inByteCount = inByteCountSave;
  721.         }
  722.     }
  723.  
  724. /* Read the archive directory into memory */
  725.  
  726. void readArcDir( const BOOLEAN doSaveDirData )
  727.     {
  728.     long securedDataLen;
  729.     int cryptInfoLen;
  730.  
  731.     /* Read in the trailer information */
  732.     readArcTrailer();
  733.     if( archiveInfo.specialInfo & SPECIAL_MULTIPART )
  734.         /* This is part of a multipart archive, prompt for the last part */
  735.         while( !( archiveInfo.specialInfo & SPECIAL_MULTIEND ) )
  736.             {
  737.             hclose( archiveFD );
  738.             archiveFD = IO_ERROR;        /* Mark it as invalid */
  739.             multipartWait( WAIT_PARTNO | WAIT_LASTPART, archiveInfo.checksum );
  740.             if( ( archiveFD = hopen( archiveFileName, O_RDONLY | S_DENYWR | A_RANDSEQ ) ) == ERROR )
  741.                 error( CANNOT_OPEN_ARCHFILE, archiveFileName );
  742.             setInputFD( archiveFD );
  743.             readArcTrailer();
  744.             }
  745.  
  746.     /* Perform bootstrap read for multipart archives */
  747.     if( archiveInfo.specialInfo & SPECIAL_MULTIEND )
  748.         {
  749.         /* Set special status flag */
  750.         flags |= MULTIPART_ARCH;
  751.  
  752.         /* Move the information from the archiveInfo fields into the segment
  753.            information which they overlay */
  754.         lastPart = archiveInfo.noFileHdrs & ~MULTIPART_SEPERATE_DISK;
  755.  
  756.         /* Read in the segmentation information */
  757.         hlseek( archiveFD, -( LONG ) ( ( lastPart * sizeof( LONG ) ) + TRAILER_SIZE ), SEEK_CUR );
  758.         resetFastIn();
  759.         checksumSetInput( ( lastPart * sizeof( LONG ) ) + TRAILER_CHK_SIZE, RESET_CHECKSUM );
  760.         readPartSizes();
  761.         getArchiveInfo( NO_READ_ID );    /* For checksumming purposes */
  762.         if( crc16 != archiveInfo.checksum )
  763.             /* Directory data was corrupted, confirm to continue */
  764.             idiotBox( MESG_ARCHIVE_DIRECTORY_CORRUPTED );
  765.  
  766.         /* We have segmentation information in memory, turn on virtual read
  767.            mode, establish current and end positions in archive */
  768.         setCurrPosition( archiveInfo.dirInfoSize );
  769.         endPosition = archiveInfo.dirInfoSize;
  770.         multipartFlags |= MULTIPART_READ;
  771.  
  772.         /* Get length of security info in correct field in case it's needed
  773.            later on */
  774.         archiveInfo.checksum = archiveInfo.noDirHdrs;
  775.  
  776.         /* If the multipart information is on a seperate disk, get the disk
  777.            with the archive itself */
  778.         if( archiveInfo.noFileHdrs & MULTIPART_SEPERATE_DISK )
  779.             getPart( lastPart );
  780.         }
  781.  
  782.     /* Set the block-mode flag if necessary */
  783.     if( archiveInfo.specialInfo & SPECIAL_BLOCK )
  784.         {
  785.         /* Make sure we don't try and change an archive using unified compression */
  786.         if( choice == ADD || choice == DELETE || choice == FRESHEN || \
  787.             choice == REPLACE || choice == UPDATE || ( flags & MOVE_FILES ) )
  788.         error( CANNOT_CHANGE_UNIFIED_ARCH );
  789.  
  790.         flags |= BLOCK_MODE;
  791.         }
  792.     else
  793.         flags &= ~BLOCK_MODE;
  794.  
  795.     /* Handle archive security if secured archive */
  796.     if( archiveInfo.specialInfo & SPECIAL_SECURED )
  797.         {
  798.         /* Read in archive trailer and security/encryption info.  The length
  799.            of this field is given by archiveTrailer.checksum, which overlays
  800.            the length field.  The length of the secured data is the current
  801.            position plus the trailer size minus the size of the ID at the
  802.            start and the ID and kludge bytes which aren't part of the trailer
  803.            any more but are at the end of the archive */
  804.         secInfoLen = archiveInfo.checksum;
  805.         vlseek( -( LONG ) ( sizeof( WORD ) + secInfoLen + \
  806.                           ( TRAILER_SIZE ) + paddingSize ), SEEK_END );
  807.         securedDataLen = vtell() + SHORT_TRAILER_SIZE - HPACK_ID_SIZE;
  808.         resetFastIn();
  809.         getArchiveInfo( NO_READ_ID );
  810.  
  811.         /* If it's a multipart archive the security info check can take
  812.            a while, so we ask the luser whether they want to do it or not */
  813.         if( !( flags & MULTIPART_ARCH ) || \
  814.             confirmAction( MESG_VERIFY_SECURITY_INFO ) )
  815.             {
  816.             if( choice == EXTRACT || choice == TEST || choice == DISPLAY )
  817.                 {
  818.                 /* Check the entire archive */
  819. #ifndef GUI
  820.                 hputs( MESG_VERIFYING_ARCHIVE_AUTHENTICITY );
  821. #endif /* !GUI */
  822.                 if( !checkSignature( 0L, securedDataLen ) )
  823.                     idiotBox( MESG_AUTH_CHECK_FAILED );
  824. #ifndef GUI
  825.                 hputchar( '\n' );
  826. #endif /* !GUI */
  827.                 }
  828.             else
  829.                 {
  830.                 /* Make sure the user wants to overwrite the security info */
  831.                 if( choice != VIEW )
  832.                     idiotBox( MESG_SECURITY_INFO_WILL_BE_DESTROYED );
  833.  
  834.                 /* Save the information for later recovery if necessary */
  835.                 if( choice == ADD )
  836.                     saveSecData();
  837.                 }
  838.             }
  839.  
  840.         /* Now treat security info as padding */
  841.         paddingSize += secInfoLen + sizeof( WORD );
  842.         }
  843.     else
  844.         if( flags & MULTIPART_ARCH )
  845.             {
  846.             /* We still need to read in the archive trailer */
  847.             vlseek( -( LONG ) SHORT_TRAILER_SIZE, SEEK_END );
  848.             resetFastIn();
  849.             getArchiveInfo( NO_READ_ID );
  850.  
  851.             /* Adjust the padding size by the difference between the full
  852.                trailer and the shortened trailer */
  853.             paddingSize -= ( TRAILER_SIZE - SHORT_TRAILER_SIZE );
  854.             }
  855.  
  856.     /* Handle encrypted archive */
  857.     if( archiveInfo.specialInfo & SPECIAL_ENCRYPTED )
  858.         {
  859.         /* Make sure we don't try to update an encrypted archive */
  860.         if( choice == ADD || choice == DELETE || \
  861.             choice == FRESHEN || choice == UPDATE || ( flags & MOVE_FILES ) )
  862.             error( CANNOT_CHANGE_ENCRYPTED_ARCH );
  863.  
  864.         /* If the luser didn't specify decryption, try it anyway */
  865.         if( !( flags & CRYPT ) )
  866.             flags |= CRYPT;
  867.  
  868.         /* Assume conventional-key encryption for now.  This may be changed
  869.            later when we read in the encryption header */
  870.         if( !( cryptFlags & ( CRYPT_CKE_ALL | CRYPT_PKE_ALL ) ) )
  871.             cryptFlags = CRYPT_CKE_ALL;
  872.         }
  873.     else
  874.         if( cryptFlags & ( CRYPT_CKE_ALL | CRYPT_PKE_ALL ) )
  875.             if( choice == DISPLAY || choice == TEST || choice == EXTRACT || choice == VIEW )
  876.                 /* The archive isn't encrypted, try processing individual
  877.                    encrypted files.  Assume conventional-key encryption for now */
  878.                 cryptFlags = CRYPT_CKE;
  879.             else
  880.                 /* Make sure we don't try to block-encrypt an unencrypted archive */
  881.                 error( CANNOT_CHANGE_UNENCRYPTED_ARCH );
  882.  
  883.     /* Check this is the correct version of archive/HPACK */
  884.     if( !isPrototype( archiveInfo.specialInfo ) )
  885.         idiotBox( "Please upgrade to a non-prototype HPACK release" );
  886.     else
  887.         if( ( archiveInfo.specialInfo & 0xE0 ) != PROTOTYPE )
  888.             /* This archive was created by a different prototype version,
  889.                confirm to continue */
  890.             idiotBox( "Peter has been messing with the filekludge again" );
  891.  
  892.     /* Read in archive directory */
  893.     if( ( ( long ) archiveInfo.dirInfoSize < 0L ) || \
  894.         ( vlseek( -( ( long ) ( archiveInfo.dirInfoSize + \
  895.                   TRAILER_SIZE + paddingSize ) ), SEEK_END ) < HPACK_ID_SIZE ) )
  896.         /* dirInfoSize has impossible value or tried to seek past archive
  897.            start, trailer information corrupted.  Note that we can actually
  898.            seek right up to the HPACK_ID at the start if the archive contains
  899.            only directories */
  900.         error( ARCHIVE_DIRECTORY_CORRUPTED );
  901. #ifndef GUI
  902.     if( flags & MULTIPART_ARCH )
  903.         hprintfs( MESG_PROCESSING_ARCHIVE_DIRECTORY );
  904. #endif /* !GUI */
  905.     arcdirCorrupted = FALSE;
  906.     resetFastIn();
  907.     if( cryptFlags & ( CRYPT_CKE_ALL | CRYPT_PKE_ALL ) )
  908.         {
  909.         preemptCryptChecksum( archiveInfo.dirInfoSize + TRAILER_CHK_SIZE );
  910.         if( !cryptSetInput( archiveInfo.dirInfoSize, &cryptInfoLen ) )
  911.             error( CANNOT_PROCESS_CRYPT_ARCH );
  912.         archiveInfo.dirInfoSize -= cryptInfoLen;
  913.         }
  914.     else
  915.         checksumSetInput( archiveInfo.dirInfoSize + TRAILER_CHK_SIZE, RESET_CHECKSUM );
  916.     readDirHeaders( archiveInfo.noDirHdrs );
  917.     readFileHeaders( archiveInfo.noFileHdrs );
  918.     readDirNames();
  919.     readFileNames();
  920.     getArchiveInfo( NO_READ_ID );    /* For checksumming purposes */
  921. #ifndef GUI
  922.     if( flags & MULTIPART_ARCH )
  923.         blankLine( 60 );
  924. #endif /* !GUI */
  925.     if( crc16 != archiveInfo.checksum || arcdirCorrupted )
  926.         {
  927.         /* Directory data was corrupted, confirm to continue */
  928.         if( cryptFlags & CRYPT_CKE_ALL )
  929.             idiotBox( MESG_ARCHIVE_DIRECTORY_WRONG_PASSWORD );
  930.         else
  931.             idiotBox( MESG_ARCHIVE_DIRECTORY_CORRUPTED );
  932.         }
  933.  
  934.     /* Move directory data to a temporary file if necessary since it will be
  935.        overwritten later */
  936.     if( doSaveDirData && dirDataOffset )
  937.         saveDirData();
  938.  
  939.     /* Only go back to the start of the archive if we have to */
  940.     if( !( flags & MULTIPART_READ ) || \
  941.         ( choice == EXTRACT || choice == TEST || choice == DISPLAY ) )
  942.         vlseek( 0L, SEEK_SET );
  943.     }
  944.  
  945. /****************************************************************************
  946. *                                                                            *
  947. *                            Write a Directory to a File                        *
  948. *                                                                            *
  949. ****************************************************************************/
  950.  
  951. /* The sizes of the encryption headers for the file and directory data */
  952.  
  953. int cryptFileDataLength, cryptDirDataLength;
  954.  
  955. /* Write the file headers to a file.  This function is split into two seperate
  956.    routines, one of which steps through the list of headers and one which
  957.    writes the headers themselves; the latter is also called to write the
  958.    error recovery information */
  959.  
  960. int writeFileHeader( FILEHDRLIST *theHeader )
  961.     {
  962.     FILEHDR *fileHeader = &theHeader->data;
  963.     WORD archInfo = fileHeader->archiveInfo;
  964.     int bytesWritten = sizeof( WORD ) + sizeof( LONG );    /* Constant fields */
  965.     int extraInfoIndex, extraInfoLen;
  966.  
  967.     /* Set the field length bits depending on the field's contents */
  968.     archInfo |= ( fileHeader->fileLen > 0xFFFF ) ? ARCH_ORIG_LEN : 0;
  969.     archInfo |= ( fileHeader->dataLen > 0xFFFF ) ? ARCH_COPR_LEN : 0;
  970.     archInfo |= ( fileHeader->dirIndex > 0xFF ) ? ARCH_OTHER_LEN : \
  971.                 ( fileHeader->auxDataLen > 0xFF ) ? ARCH_OTHER_HI : \
  972.                 ( fileHeader->dirIndex || fileHeader->auxDataLen ) ? \
  973.                   ARCH_OTHER_LO : 0;
  974.  
  975.     /* Write the header itself */
  976.     fputWord( archInfo );
  977.     switch( archInfo & ARCH_OTHER_LEN )
  978.         {
  979.         case ARCH_OTHER_BYTE_BYTE:
  980.             /* dirIndex = BYTE, auxDataLen = BYTE */
  981.             fputByte( ( BYTE ) fileHeader->dirIndex );
  982.             fputByte( ( BYTE ) fileHeader->auxDataLen );
  983.             bytesWritten += sizeof( BYTE ) + sizeof( BYTE );
  984.             break;
  985.  
  986.         case ARCH_OTHER_BYTE_WORD:
  987.             /* dirIndex = BYTE, auxDataLen = WORD */
  988.             fputByte( ( BYTE ) fileHeader->dirIndex );
  989.             fputWord( ( WORD ) fileHeader->auxDataLen );
  990.             bytesWritten += sizeof( BYTE ) + sizeof( WORD );
  991.             break;
  992.  
  993.         case ARCH_OTHER_WORD_LONG:
  994.             /* dirIndex = WORD, auxDataLen = LONG */
  995.             fputWord( fileHeader->dirIndex );
  996.             fputLong( fileHeader->auxDataLen );
  997.             bytesWritten += sizeof( WORD ) + sizeof( LONG );
  998.         }
  999.     fputLong( fileHeader->fileTime );
  1000.     if( archInfo & ARCH_ORIG_LEN )
  1001.         {
  1002.         fputLong( fileHeader->fileLen );
  1003.         bytesWritten += sizeof( LONG );
  1004.         }
  1005.     else
  1006.         {
  1007.         fputWord( ( WORD ) fileHeader->fileLen );
  1008.         bytesWritten += sizeof( WORD );
  1009.         }
  1010.     if( archInfo  & ARCH_COPR_LEN )
  1011.         {
  1012.         fputLong( fileHeader->dataLen );
  1013.         bytesWritten += sizeof( LONG );
  1014.         }
  1015.     else
  1016.         {
  1017.         fputWord( ( WORD ) fileHeader->dataLen );
  1018.         bytesWritten += sizeof( WORD );
  1019.         }
  1020.  
  1021.     /* Handle type SPECIAL files by writing an additional WORD containing
  1022.        the file's hType */
  1023.     if( archInfo & ARCH_SPECIAL )
  1024.         {
  1025.         fputWord( theHeader->hType );
  1026.         bytesWritten += sizeof( WORD );
  1027.         }
  1028.  
  1029.     /* Handle any extra info attached to the header */
  1030.     if( archInfo & ARCH_EXTRAINFO )
  1031.         {
  1032.         /* Write out extraInfo byte */
  1033.         extraInfoIndex = 1;
  1034.         extraInfoLen = getExtraInfoLen( *theHeader->extraInfo );
  1035.         fputByte( *theHeader->extraInfo );
  1036.         bytesWritten += sizeof( BYTE ) + extraInfoLen;
  1037.  
  1038. #if defined( __MSDOS__ )
  1039.         /* Fix compiler bug */
  1040. #elif defined( __ARC__ )
  1041.         /* Write file type */
  1042.         if( extraInfoLen >= sizeof( WORD ) )
  1043.             {
  1044.             extraInfoLen -= sizeof( WORD );
  1045.             extraInfoIndex += sizeof( WORD );
  1046.             }
  1047.         fputWord( theHeader->type );
  1048. #elif defined( __IIGS__ )
  1049.         /* Write file type and auxiliary type */
  1050.         if( extraInfoLen >= sizeof( WORD ) + sizeof( LONG ) )
  1051.             {
  1052.             extraInfoLen -= sizeof( WORD ) + sizeof( LONG );
  1053.             extraInfoIndex += sizeof( WORD ) + sizeof( LONG );
  1054.             }
  1055.         fputWord( theHeader->type );
  1056.         fputLong( theHeader->auxType );
  1057. #elif defined( __MAC__ )
  1058.         /* Write file type and creator */
  1059.         if( extraInfoLen >= sizeof( LONG ) + sizeof( LONG ) )
  1060.             {
  1061.             extraInfoLen -= sizeof( LONG ) + sizeof( LONG );
  1062.             extraInfoIndex += sizeof( LONG ) + sizeof( LONG );
  1063.             }
  1064.         fputLong( theHeader->type );
  1065.         fputLong( theHeader->creator );
  1066. #elif defined( __UNIX__ )
  1067.         /* Write link ID */
  1068.         if( extraInfoLen >= sizeof( WORD ) )
  1069.             {
  1070.             extraInfoLen -= sizeof( WORD );
  1071.             extraInfoIndex += sizeof( WORD );
  1072.             }
  1073.         fputWord( theHeader->fileLinkID );
  1074. #endif /* Various OS-dependant extraInfo reads */
  1075.  
  1076.         /* Write out any remaining info */
  1077.         while( extraInfoLen-- )
  1078.             fputByte( theHeader->extraInfo[ extraInfoIndex++ ] );
  1079.         }
  1080.  
  1081.     return( bytesWritten );
  1082.     }
  1083.  
  1084. static LONG writeFileHeaders( void )
  1085.     {
  1086.     LONG bytesWritten = 0L;
  1087.  
  1088.     archiveInfo.noFileHdrs = 0;
  1089.  
  1090.     /* Write a dummy header for the encryption information if necessary */
  1091.     if( cryptFileDataLength )
  1092.         {
  1093.         fputWord( ARCH_SPECIAL );
  1094.         fputLong( getStrongRandomLong() );
  1095.         fputWord( getStrongRandomWord() );
  1096.         fputWord( cryptFileDataLength );
  1097.         fputWord( TYPE_DUMMY );
  1098.         bytesWritten += sizeof( WORD ) + sizeof( LONG ) + sizeof( WORD ) + \
  1099.                         sizeof( WORD ) + sizeof( WORD );
  1100.         archiveInfo.noFileHdrs++;
  1101.         }
  1102.  
  1103.     /* Write the file headers themselves */
  1104.     for( fileHdrCurrPtr = fileHdrStartPtr; fileHdrCurrPtr != NULL; \
  1105.          fileHdrCurrPtr = fileHdrCurrPtr->next )
  1106.         {
  1107.         bytesWritten += writeFileHeader( fileHdrCurrPtr );
  1108.         archiveInfo.noFileHdrs++;
  1109.         }
  1110.  
  1111.     return( bytesWritten );
  1112.     }
  1113.  
  1114. /* Write the filenames to a file */
  1115.  
  1116. static LONG writeFileNames( void )
  1117.     {
  1118.     LONG bytesWritten = 0L;
  1119.     char *strPtr;
  1120.  
  1121.     /* Write a dummy name for the encryption header if necessary */
  1122.     if( cryptFileDataLength )
  1123.         {
  1124.         fputByte( '\0' );
  1125.         bytesWritten++;
  1126.         }
  1127.  
  1128.     /* Write file names */
  1129.     for( fileHdrCurrPtr = fileHdrStartPtr; fileHdrCurrPtr != NULL; \
  1130.          fileHdrCurrPtr = fileHdrCurrPtr->next )
  1131.         {
  1132.         strPtr = fileHdrCurrPtr->fileName;
  1133.         do
  1134.             {
  1135.             fputByte( *strPtr );
  1136.             bytesWritten++;
  1137.             }
  1138.         while( *strPtr++ );
  1139.         }
  1140.  
  1141.     return( bytesWritten );
  1142.     }
  1143.  
  1144. /* Write the directory headers to a file */
  1145.  
  1146. static LONG writeDirHeaders( void )
  1147.     {
  1148.     WORD theEntry, hType, linkID;
  1149.     LONG bytesWritten = 0L;
  1150.     DIRHDR *dirHeader;
  1151.     BYTE dirInfo;
  1152.  
  1153.     archiveInfo.noDirHdrs = 0;
  1154.  
  1155.     /* Write a dummy header for the encryption information if necessary */
  1156.     if( cryptDirDataLength )
  1157.         {
  1158.         fputByte( DIR_OTHER_BYTE_BYTE | DIR_SPECIAL );
  1159.         fputByte( ROOT_DIR );
  1160.         fputByte( cryptDirDataLength );
  1161.         fputLong( getStrongRandomLong() );
  1162.         fputWord( DIRTYPE_DUMMY );
  1163.         bytesWritten += sizeof( BYTE ) + sizeof( BYTE ) + sizeof( BYTE ) + \
  1164.                         sizeof( LONG ) + sizeof( WORD );
  1165.         archiveInfo.noDirHdrs++;
  1166.         }
  1167.  
  1168.     /* Write the directory headers themselves */
  1169.     for( theEntry = dirHdrList[ ROOT_DIR ]->next; theEntry != END_MARKER; \
  1170.          theEntry = dirHdrList[ theEntry ]->next )
  1171.         {
  1172.         /* Pull out relevant info from directory header */
  1173.         dirHeader = &dirHdrList[ theEntry ]->data;
  1174.         hType = dirHdrList[ theEntry ]->hType;
  1175.         linkID = dirHdrList[ theEntry ]->linkID;
  1176.  
  1177.         /* Set the field length bits depending on the field's contents */
  1178.         dirInfo = dirHeader->dirInfo;
  1179.         dirInfo |= ( dirHeader->parentIndex > 0xFF ) ? DIR_OTHER_LEN : \
  1180.                    ( dirHeader->dataLen > 0xFF ) ? DIR_OTHER_HI : \
  1181.                    ( dirHeader->parentIndex || dirHeader->dataLen ) ? \
  1182.                      DIR_OTHER_LO : 0;
  1183.  
  1184.         /* Write the header itself */
  1185.         fputByte( dirInfo );
  1186.         switch( dirInfo & DIR_OTHER_LEN )
  1187.             {
  1188.             case DIR_OTHER_BYTE_BYTE:
  1189.                 /* parentIndex = BYTE, dataLen = BYTE */
  1190.                 fputByte( ( BYTE ) dirHeader->parentIndex );
  1191.                 fputByte( ( BYTE ) dirHeader->dataLen );
  1192.                 bytesWritten += sizeof( BYTE ) + sizeof( BYTE );
  1193.                 break;
  1194.  
  1195.             case DIR_OTHER_BYTE_WORD:
  1196.                 /* parentIndex = BYTE, dataLen = WORD */
  1197.                 fputByte( ( BYTE ) dirHeader->parentIndex );
  1198.                 fputWord( ( WORD ) dirHeader->dataLen );
  1199.                 bytesWritten += sizeof( BYTE ) + sizeof( WORD );
  1200.                 break;
  1201.  
  1202.             case DIR_OTHER_WORD_LONG:
  1203.                 /* dirIndex = WORD, auxDataLen = LONG */
  1204.                 fputWord( dirHeader->parentIndex );
  1205.                 fputLong( dirHeader->dataLen );
  1206.                 bytesWritten += sizeof( WORD ) + sizeof( LONG );
  1207.             }
  1208.         fputLong( dirHeader->dirTime );
  1209.         bytesWritten += sizeof( BYTE ) + sizeof( LONG );
  1210.  
  1211.         /* Handle type SPECIAL directories by writing an additional WORD
  1212.            containing the directories's hType */
  1213.         if( dirInfo & DIR_SPECIAL )
  1214.             {
  1215.             fputWord( hType );
  1216.             bytesWritten += sizeof( WORD );
  1217.             }
  1218.  
  1219.         /* Handle linked directories by writing the linkID */
  1220.         if( dirInfo & DIR_LINKED )
  1221.             {
  1222.             fputWord( linkID );
  1223.             bytesWritten += sizeof( WORD );
  1224.             }
  1225.  
  1226.         archiveInfo.noDirHdrs++;
  1227.         }
  1228.  
  1229.     return( bytesWritten );
  1230.     }
  1231.  
  1232. /* Write the directory names to a file */
  1233.  
  1234. static LONG writeDirNames( void )
  1235.     {
  1236.     LONG bytesWritten = 0L;
  1237.     WORD theEntry;
  1238.     WORD *wordPtr;
  1239.     char *strPtr;
  1240.  
  1241.     /* Write a dummy name for the encryption header if necessary */
  1242.     if( cryptDirDataLength )
  1243.         {
  1244.         fputByte( '\0' );
  1245.         bytesWritten++;
  1246.         }
  1247.  
  1248.     /* Write directory names */
  1249.     for( theEntry = dirHdrList[ ROOT_DIR ]->next; theEntry != END_MARKER; \
  1250.          theEntry = dirHdrList[ theEntry ]->next )
  1251.         if( dirHdrList[ theEntry ]->data.dirInfo & DIR_UNICODE )
  1252.             {
  1253.             /* Write Unicode string */
  1254.             wordPtr = ( WORD * ) dirHdrList[ theEntry ]->dirName;
  1255.             do
  1256.                 {
  1257.                 fputWord( *wordPtr );
  1258.                 bytesWritten += sizeof( WORD );
  1259.                 }
  1260.             while( *wordPtr++ );
  1261.             }
  1262.         else
  1263.             {
  1264.             /* Write ASCII/EBCDIC string */
  1265.             strPtr = dirHdrList[ theEntry ]->dirName;
  1266.             do
  1267.                 {
  1268.                 fputByte( *strPtr );
  1269.                 bytesWritten++;
  1270.                 }
  1271.             while( *strPtr++ );
  1272.         }
  1273.  
  1274.     return( bytesWritten );
  1275.     }
  1276.  
  1277. /* Write the size of each part in a multi-part archive */
  1278.  
  1279. static void writePartSizes( void )
  1280.     {
  1281.     if( flags & MULTIPART_ARCH )
  1282.         {
  1283.         /* Flag the fact that this is the last part of a multipart archive */
  1284.         archiveInfo.specialInfo |= SPECIAL_MULTIEND;
  1285.  
  1286.         /* Remember where the segmentation information starts */
  1287.         segmentEnd = getCurrPosition();
  1288.  
  1289.         /* Write out the size of each section */
  1290.         checksumBegin( RESET_CHECKSUM );
  1291.         for( partSizeCurrPtr = partSizeStartPtr; partSizeCurrPtr != NULL; \
  1292.              partSizeCurrPtr = partSizeCurrPtr->next )
  1293.             fputLong( partSizeCurrPtr->segEnd );
  1294.         }
  1295.     }
  1296.  
  1297. /* Write the directory size information */
  1298.  
  1299. static void writeArchiveInfo( void )
  1300.     {
  1301.     fputWord( archiveInfo.noDirHdrs );
  1302.     fputWord( archiveInfo.noFileHdrs );
  1303.     fputLong( archiveInfo.dirInfoSize );
  1304.  
  1305.     /* Calculate checksum for any remaining archive directory data before
  1306.        this point and write it */
  1307.     checksumEnd();
  1308.     fputWord( crc16 );
  1309.     }
  1310.  
  1311. /* Write the final archive trailer */
  1312.  
  1313. static WORD savedCRC;
  1314. static BOOLEAN saveCRC;
  1315.  
  1316. static void writeArchiveTrailer( void )
  1317.     {
  1318.     /* Write the information for the multipart read bootstrap if necessary */
  1319.     if( flags & MULTIPART_ARCH )
  1320.         {
  1321.         /* In case we call writeArchiveTrailer() a second time with altered
  1322.            trailer info we calculate the data checksum to this point and save
  1323.            it now */
  1324.         if( saveCRC )
  1325.             {
  1326.             checksumEnd();
  1327.             savedCRC = crc16;
  1328.             }
  1329.         else
  1330.             crc16 = savedCRC;
  1331.  
  1332.         /* Write length of authentication/encryption info, total number of
  1333.            segments, and end of last segment */
  1334.         checksumBegin( NO_RESET_CHECKSUM );
  1335.         fputWord( 0 );
  1336.         fputWord( lastPart );
  1337.         fputLong( segmentEnd );
  1338.         checksumEnd();
  1339.  
  1340.         /* Calculate checksum for any remaining segment data before
  1341.            this point and write it */
  1342.         fputWord( crc16 );
  1343.         }
  1344.  
  1345.     /* Write the kludge byte */
  1346.     if( flags & BLOCK_MODE )
  1347.         archiveInfo.specialInfo |= SPECIAL_BLOCK;
  1348.     if( cryptFlags & ( CRYPT_CKE_ALL | CRYPT_PKE_ALL ) )
  1349.         archiveInfo.specialInfo |= SPECIAL_ENCRYPTED;
  1350.     if( cryptFlags & CRYPT_SIGN_ALL )
  1351.         archiveInfo.specialInfo |= SPECIAL_SECURED;
  1352.     fputByte( archiveInfo.specialInfo );
  1353.  
  1354.     /* We must use fputByte() to put the archive ID since fputLong() will do
  1355.        endianness conversion */
  1356.     fputByte( HPACK_ID[ 0 ] );
  1357.     fputByte( HPACK_ID[ 1 ] );
  1358.     fputByte( HPACK_ID[ 2 ] );
  1359.     fputByte( HPACK_ID[ 3 ] );
  1360.     }
  1361.  
  1362. /* Write the directory data to the archive */
  1363.  
  1364. static void writeDirData( void )
  1365.     {
  1366.     LONG position = 0L, offset, delta, dataLen;
  1367.     WORD theEntry, multipartFlagSave = multipartFlags;
  1368.  
  1369.     /* Turn off multipart reads when saving the directory data */
  1370.     multipartFlags &= ~MULTIPART_READ;
  1371.  
  1372.     /* Write out any data still in the buffer */
  1373.     flushDirBuffer();
  1374.  
  1375.     /* Move the directory data from the temporary file to the archive file.
  1376.        We use a loop with fputByte() instead of a moveData() call to take care
  1377.        of buffering and handle encryption for us */
  1378.     if( dirDataOffset )
  1379.         {
  1380.         if( cryptFlags & ( CRYPT_CKE_ALL | CRYPT_PKE_ALL ) )
  1381.             cryptFileDataLength = cryptBegin( ( cryptFlags & CRYPT_SEC ) ? \
  1382.                                               SECONDARY_KEY : MAIN_KEY );
  1383.  
  1384.         /* Step through the list of headers moving the data corresponding to
  1385.            the header from the dirFile to the archive file.  Doing things
  1386.            this way is necessary since fixEverything() may have reordered the
  1387.            directory headers so that their order no longer corresponds to the
  1388.            data in the dirFile */
  1389.         setInputFD( dirFileFD );
  1390.         hlseek( dirFileFD, 0L, SEEK_SET );
  1391.         forceRead();
  1392.         for( theEntry = dirHdrList[ ROOT_DIR ]->next; theEntry != END_MARKER; \
  1393.              theEntry = dirHdrList[ theEntry ]->next )
  1394.             /* Only do the move if there's something to move */
  1395.             if( dataLen = dirHdrList[ theEntry ]->data.dataLen )
  1396.                 {
  1397.                 if( ( offset = dirHdrList[ theEntry ]->offset ) < position )
  1398.                     {
  1399.                     /* Data is behind us, move backwards in dirFile */
  1400.                     delta = position - offset;
  1401.                     if( _inByteCount >= delta )
  1402.                         /* Data is still in buffer; move backwards to it */
  1403.                         _inByteCount -= ( int ) delta;
  1404.                     else
  1405.                         {
  1406.                         /* Data is outside buffer, go to position */
  1407.                         hlseek( dirFileFD, offset, SEEK_SET );
  1408.                         forceRead();
  1409.                         }
  1410.                     }
  1411.                 else
  1412.                     /* Data is ahead of us; skipSeek to it if necessary */
  1413.                     if( offset - position )
  1414.                         skipSeek( offset - position );
  1415.  
  1416.                 /* Move the data across and update position */
  1417.                 position = offset + dataLen;
  1418.                 while( dataLen-- )
  1419.                     /* Not very elegant but it nicely takes care of buffering */
  1420.                     fputByte( fgetByte() );
  1421.                 }
  1422.  
  1423.         if( cryptFlags & ( CRYPT_CKE_ALL | CRYPT_PKE_ALL ) )
  1424.             cryptEnd();
  1425.         }
  1426.     else
  1427.         /* Reset encryption header status */
  1428.         cryptDirDataLength = 0;
  1429.  
  1430.     multipartFlags = multipartFlagSave;
  1431.  
  1432.     /* Zap the dirFile if it exists */
  1433.     if( dirFileFD != IO_ERROR )
  1434.         {
  1435.         hclose( dirFileFD );
  1436.         hunlink( dirFileName );
  1437.         dirFileFD = IO_ERROR;        /* Mark it as invalid */
  1438.         }
  1439.     }
  1440.  
  1441. /* Write the archive directory to a file */
  1442.  
  1443. void writeArcDir( void )
  1444.     {
  1445.     /* Initially we have no crypt information */
  1446.     cryptFileDataLength = cryptDirDataLength = 0;
  1447.  
  1448.     /* Clean up the directory structure if it's safe to do so */
  1449.     if( doFixEverything )
  1450.         fixEverything();
  1451.  
  1452.     /* Write the directory data (if any) to the archive */
  1453.     writeDirData();
  1454.  
  1455.     /* Build up archive trailer information and write it.  Note that the
  1456.        cryptBegin() call must be before the checksumBegin() call since the
  1457.        encryption information is covered by its own checksum */
  1458.     archiveInfo.specialInfo = 0;
  1459.     archiveInfo.dirInfoSize = 0L;
  1460.     if( cryptFlags & ( CRYPT_CKE_ALL | CRYPT_PKE_ALL ) )
  1461.         archiveInfo.dirInfoSize = ( LONG ) cryptBegin( MAIN_KEY );
  1462.     checksumBegin( RESET_CHECKSUM );
  1463.     archiveInfo.dirInfoSize += writeDirHeaders();
  1464.     archiveInfo.dirInfoSize += writeFileHeaders();
  1465.     archiveInfo.dirInfoSize += writeDirNames();
  1466.     archiveInfo.dirInfoSize += writeFileNames();
  1467.     if( cryptFlags & ( CRYPT_CKE_ALL | CRYPT_PKE_ALL ) )
  1468.         cryptEnd();
  1469.     archiveInfo.specialInfo |= PROTOTYPE;
  1470.     writeArchiveInfo();        /* Write directory size information */
  1471.     if( multipartFlags & MULTIPART_WRITE )
  1472.         {
  1473.         /* Force the evaluation of lastPart and try and write the
  1474.            segmentation information and final trailer as one block */
  1475.         setWriteType( SAFE_WRITE );
  1476.         flushBuffer();            /* Force lastPart evaluation */
  1477.         saveCRC = TRUE;
  1478.         writePartSizes();
  1479.         writeArchiveTrailer();    /* Put info in buffer */
  1480.         setWriteType( ATOMIC_WRITE );
  1481.         flushBuffer();            /* Write info as one block */
  1482.  
  1483.         if( !atomicWriteOK )
  1484.             {
  1485.             /* We need to put the information on a seperate disk.  Set the
  1486.                high bit of lastPart to flag the fact */
  1487.             _outByteCount -= SHORT_TRAILER_SIZE + sizeof( BYTE ) + HPACK_ID_SIZE;
  1488.             lastPart |= MULTIPART_SEPERATE_DISK;
  1489.             saveCRC = FALSE;
  1490.             writeArchiveTrailer();    /* Re-put trailer info in buffer */
  1491.             flushBuffer();            /* Write data to single block on disk */
  1492.             saveCRC = TRUE;
  1493.             }
  1494.  
  1495.         setWriteType( STD_WRITE );
  1496.         }
  1497.     else
  1498.         {
  1499.         /* Sign the archive if required */
  1500.         if( cryptFlags & CRYPT_SIGN_ALL )
  1501.             {
  1502.             flushBuffer();        /* Get all info to sign out of buffers */
  1503.             fputWord( createSignature( 0L, getCurrPosition(), signerID ) );
  1504.             }
  1505.  
  1506.         /* Write final trailer information */
  1507.         writeArchiveTrailer();
  1508.         flushBuffer();
  1509.         }
  1510.  
  1511.     /* If we've ended up deleting all the files in the archive, delete
  1512.        the archive itself */
  1513.     if( ( archiveInfo.noDirHdrs | archiveInfo.noFileHdrs ) == 0 )
  1514.         {
  1515.         errorFD = ERROR;
  1516.         hunlink( errorFileName );
  1517.         }
  1518.  
  1519.     /* If it's a multipart archive and there is only one part, redo it
  1520.        as a normal archive */
  1521.     if( ( flags & MULTIPART_ARCH ) && !lastPart )
  1522.         {
  1523.         /* Move back over output padding and trailer and truncate archive */
  1524.         hlseek( archiveFD, -( long ) ( MULTIPART_TRAILER_SIZE + SHORT_TRAILER_SIZE + \
  1525.                                        sizeof( BYTE ) + HPACK_ID_SIZE ), SEEK_END );
  1526.         htruncate( archiveFD );
  1527.  
  1528.         /* Reset its status to a normal single-part archive and rewrite
  1529.            the trailer */
  1530.         resetFastOut();
  1531.         flags &= ~MULTIPART_ARCH;
  1532.         multipartFlags = 0;
  1533.         archiveInfo.specialInfo &= ~SPECIAL_MULTIEND;
  1534.         writeArchiveTrailer();
  1535.         flushBuffer();
  1536.         }
  1537.     }
  1538.